const LAST_CONNECTED_DEVICE = "last_connected_device";
const TextEncoder = require("text-encoding/index").TextEncoder;

function toArrayBuffer(input) {
  switch (typeof input) {
    case "string":
      const to = new TextEncoder("GBK", {
        NONSTANDARD_allowLegacyEncoding: true,
      });
      return to.encode(input);
    case "number":
      return Uint8Array.of(input);
    default:
      if (input instanceof Array) {
        return new Uint8Array(input);
      } else {
        console.log("无效的数据类型");
      }
  }
}

function ArrayBufferCat(resultConstructor, ...arrays) {
  let totalLength = 0;
  for (let arr of arrays) {
    totalLength += arr.length;
  }
  let result = new resultConstructor(totalLength);
  let offset = 0;
  for (let arr of arrays) {
    result.set(arr, offset);
    offset += arr.length;
  }
  return result;
}

function inArray(arr, key, val) {
  for (let i = 0; i < arr.length; i++) {
    if (arr[i][key] === val) {
      return i;
    }
  }
  return -1;
}

// ArrayBuffer转16进度字符串示例
function ab2hex(buffer) {
  const hexArr = Array.prototype.map.call(
    new Uint8Array(buffer),
    function (bit) {
      return ("00" + bit.toString(16)).slice(-2);
    }
  );
  return hexArr.join(",");
}

function str2ab(str) {
  // Convert str to ArrayBuff and write to printer
  let buffer = new ArrayBuffer(str.length);
  let dataView = new DataView(buffer);
  for (let i = 0; i < str.length; i++) {
    dataView.setUint8(i, str.charAt(i).charCodeAt(0));
  }
  return buffer;
}

Page({
  data: {
    devices: [],
    connected: false,
    chs: [],
  },
  onUnload() {
    this.closeBluetoothAdapter();
  },
  openBluetoothAdapter() {
    if (!wx.openBluetoothAdapter) {
      wx.showModal({
        title: "提示",
        content:
          "当前微信版本过低，无法使用该功能，请升级到最新微信版本后重试。",
      });
      return;
    }
    wx.openBluetoothAdapter({
      success: (res) => {
        console.log("openBluetoothAdapter success", res);
        this.startBluetoothDevicesDiscovery();
      },
      fail: (res) => {
        console.log("openBluetoothAdapter fail", res);
        if (res.errCode === 10001) {
          wx.showModal({
            title: "错误",
            content: "未找到蓝牙设备, 请打开蓝牙后重试。",
            showCancel: false,
          });
          wx.onBluetoothAdapterStateChange((res) => {
            console.log("onBluetoothAdapterStateChange", res);
            if (res.available) {
              // 取消监听，否则stopBluetoothDevicesDiscovery后仍会继续触发onBluetoothAdapterStateChange，
              // 导致再次调用startBluetoothDevicesDiscovery
              wx.onBluetoothAdapterStateChange(() => {});
              this.startBluetoothDevicesDiscovery();
            }
          });
        }
      },
    });
    wx.onBLEConnectionStateChange((res) => {
      // 该方法回调中可以用于处理连接意外断开等异常情况
      console.log(
        "onBLEConnectionStateChange",
        `device ${res.deviceId} state has changed, connected: ${res.connected}`
      );
      this.setData({
        connected: res.connected,
      });
      if (!res.connected) {
        wx.showModal({
          title: "错误",
          content: "蓝牙连接已断开",
          showCancel: false,
        });
      }
    });
  },
  getBluetoothAdapterState() {
    wx.getBluetoothAdapterState({
      success: (res) => {
        console.log("getBluetoothAdapterState", res);
        if (res.discovering) {
          this.onBluetoothDeviceFound();
        } else if (res.available) {
          this.startBluetoothDevicesDiscovery();
        }
      },
    });
  },
  startBluetoothDevicesDiscovery() {
    if (this._discoveryStarted) {
      return;
    }
    this._discoveryStarted = true;
    wx.startBluetoothDevicesDiscovery({
      success: (res) => {
        console.log("startBluetoothDevicesDiscovery success", res);
        this.onBluetoothDeviceFound();
      },
      fail: (res) => {
        console.log("startBluetoothDevicesDiscovery fail", res);
      },
    });
  },
  stopBluetoothDevicesDiscovery() {
    wx.stopBluetoothDevicesDiscovery({
      complete: () => {
        console.log("stopBluetoothDevicesDiscovery");
        this._discoveryStarted = false;
      },
    });
  },
  onBluetoothDeviceFound() {
    wx.onBluetoothDeviceFound((res) => {
      res.devices.forEach((device) => {
        if (!device.name && !device.localName) {
          return;
        }
        const foundDevices = this.data.devices;
        const idx = inArray(foundDevices, "deviceId", device.deviceId);
        const data = {};
        if (idx === -1) {
          data[`devices[${foundDevices.length}]`] = device;
        } else {
          data[`devices[${idx}]`] = device;
        }
        this.setData(data);
      });
    });
  },
  createBLEConnection(e) {
    const ds = e.currentTarget.dataset;
    const deviceId = ds.deviceId;
    const name = ds.name;
    this._createBLEConnection(deviceId, name);
  },
  _createBLEConnection(deviceId, name) {
    wx.showLoading();
    wx.createBLEConnection({
      deviceId,
      success: () => {
        console.log("createBLEConnection success");
        this.setData({
          connected: true,
          name,
          deviceId,
        });
        this.getBLEDeviceServices(deviceId);
        wx.setStorage({
          key: LAST_CONNECTED_DEVICE,
          data: name + ":" + deviceId,
        });
      },
      complete() {
        wx.hideLoading();
      },
      fail: (res) => {
        console.log("createBLEConnection fail", res);
      },
    });
    this.stopBluetoothDevicesDiscovery();
  },
  closeBLEConnection() {
    wx.closeBLEConnection({
      deviceId: this.data.deviceId,
    });
    this.setData({
      connected: false,
      chs: [],
      canWrite: false,
    });
  },
  getBLEDeviceServices(deviceId) {
    wx.getBLEDeviceServices({
      deviceId,
      success: (res) => {
        console.log("getBLEDeviceServices", res);
        for (let i = 0; i < res.services.length; i++) {
          if (res.services[i].isPrimary) {
            this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid);
            return;
          }
        }
      },
    });
  },
  getBLEDeviceCharacteristics(deviceId, serviceId) {
    wx.getBLEDeviceCharacteristics({
      deviceId,
      serviceId,
      success: (res) => {
        console.log("getBLEDeviceCharacteristics success", res.characteristics);
        // 这里会存在特征值是支持write，写入成功但是没有任何反应的情况
        // 只能一个个去试
        for (let i = 0; i < res.characteristics.length; i++) {
          const item = res.characteristics[i];
          if (item.properties.write) {
            this.setData({
              canWrite: true,
            });
            this._deviceId = deviceId;
            this._serviceId = serviceId;
            this._characteristicId = item.uuid;
            break;
          }
        }
      },
      fail(res) {
        console.error("getBLEDeviceCharacteristics", res);
      },
    });
  },
  writeBLECharacteristicValue() {
    let buffer = ArrayBufferCat(
      Uint8Array,
      toArrayBuffer("! 0 200 200 750 1\r\n"),
      toArrayBuffer("SETMAG 2 3\r\n"),
      toArrayBuffer(`TEXT 10 0 0 150 测试打印数据第一行\r\n`),
      toArrayBuffer("SETMAG 2 2\r\n"),
      toArrayBuffer(`TEXT 10 0 0 270 打印内容加上英文数字等rafg1345\r\n`),
      toArrayBuffer("SETMAG 2 2\r\n"),
      toArrayBuffer(`TEXT 10 0 0 350 打印第三行\r\n`),
      toArrayBuffer("SETMAG 2 2\r\n"),
      toArrayBuffer("B QR 0 250 M 2 U 3\r\n"),
      toArrayBuffer("MA, https://www.docin.com/p-2160105026.html\r\n"),
      toArrayBuffer("ENDQR\r\n"),
      toArrayBuffer("FORM\r\n"),
      toArrayBuffer("PRINT\r\n")
    ).buffer;

    console.log(
      "ArrayBuffer",
      "length: " + buffer.byteLength,
      " hex: " + ab2hex(buffer)
    );
    // 1.并行调用多次会存在写失败的可能性
    // 2.建议每次写入不超过20字节
    // 分包处理，延时调用
    const maxChunk = 20;
    const delay = 20;
    for (
      let i = 0, j = 0, length = buffer.byteLength;
      i < length;
      i += maxChunk, j++
    ) {
      let subPackage = buffer.slice(
        i,
        i + maxChunk <= length ? i + maxChunk : length
      );
      setTimeout(this._writeBLECharacteristicValue, j * delay, subPackage);
    }
  },
  _writeBLECharacteristicValue(buffer) {
    wx.writeBLECharacteristicValue({
      deviceId: this._deviceId,
      serviceId: this._serviceId,
      characteristicId: this._characteristicId,
      value: buffer,
      success(res) {
        console.log("writeBLECharacteristicValue success", res);
      },
      fail(res) {
        console.log("writeBLECharacteristicValue fail", res);
      },
    });
  },
  closeBluetoothAdapter() {
    wx.closeBluetoothAdapter();
    this._discoveryStarted = false;
  },
  onLoad(options) {
    const lastDevice = wx.getStorageSync(LAST_CONNECTED_DEVICE);
    this.setData({
      lastDevice: lastDevice,
    });
  },
  createBLEConnectionWithDeviceId(e) {
    // 小程序在之前已有搜索过某个蓝牙设备，并成功建立连接，可直接传入之前搜索获取的 deviceId 直接尝试连接该设备
    const device = this.data.lastDevice;
    if (!device) {
      return;
    }
    const index = device.indexOf(":");
    const name = device.substring(0, index);
    const deviceId = device.substring(index + 1, device.length);
    console.log("createBLEConnectionWithDeviceId", name + ":" + deviceId);
    wx.openBluetoothAdapter({
      success: (res) => {
        console.log("openBluetoothAdapter success", res);
        this._createBLEConnection(deviceId, name);
      },
      fail: (res) => {
        console.log("openBluetoothAdapter fail", res);
        if (res.errCode === 10001) {
          wx.showModal({
            title: "错误",
            content: "未找到蓝牙设备, 请打开蓝牙后重试。",
            showCancel: false,
          });
          wx.onBluetoothAdapterStateChange((res) => {
            console.log("onBluetoothAdapterStateChange", res);
            if (res.available) {
              // 取消监听
              wx.onBluetoothAdapterStateChange(() => {});
              this._createBLEConnection(deviceId, name);
            }
          });
        }
      },
    });
  },
});
